﻿using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Web.Security;
using Lbb.Common;
using System.Runtime.Caching;
using System.Threading;
using Lbb.User.Localization;

namespace Lbb.User
{
    public partial class MemberUser
    {
        public readonly static ObjectCache Cache = new MemoryCache("MemberUserCache");

        #region Ctors
        static MemberUser()
        {
            var provider = Membership.Provider as Lbb.User.Providers.SqlMembershipProvider;
            if (provider == null) throw new Exception(MemberUserRes.ProviderTypeError);
            provider.CreatedUser += provider_CreatedUser;
            provider.DeletingUser += provider_DeletingUser;
            provider.DeletedUser += provider_DeletedUser;
        }
        #endregion Ctors

        #region Static service methods

        /// <summary>
        /// 注册用户
        /// </summary>
        /// <param name="username">用户名</param>
        /// <param name="email">邮箱</param>
        /// <param name="password">密码</param>
        /// <param name="autoLogin">自动登录</param>
        /// <param name="throwError">抛出异常</param>
        /// <returns></returns>
        public static MemberUser Register(string username, string email, string password, bool autoLogin = true, bool throwError = false)
        {
            if (string.IsNullOrEmpty(username)) throw new ArgumentNullException("username");
            if (string.IsNullOrEmpty(email)) throw new ArgumentNullException("email");
            if (string.IsNullOrEmpty(password)) throw new ArgumentNullException("password");

            #region Event Registering
            if (Registering != null)
            {
                var eventArgs = new UserEventArgs
                {
                    ExtArgDict = new Dictionary<string, object>
                    {
                        {"username",username},
                        {"email",email},
                        {"password",password},
                        {"autoLogin",autoLogin}
                    }
                };

                Registering.Invoke(null, eventArgs);
                if (eventArgs.Cancel) return null;
            }
            #endregion Event Registering

            MembershipCreateStatus mcs;
            var mUser = Membership.CreateUser(username, password, email, UserConfig.DefQuestion, UserConfig.DefAnswer, true, out mcs);
            if (mcs != MembershipCreateStatus.Success)
            {
                if (throwError) throw new Exception(AccountValidation.ErrorCodeToString(mcs));
                return null;
            }
            var hUser = new MemberUser(mUser);

            #region Event Registered
            if (Registered != null)
            {
                var eventArgs = new UserEventArgs
                {
                    User = hUser,
                    ExtArgDict = new Dictionary<string, object>
                    {
                        {"password",password}
                    }
                };
                try
                {
                    Registered.Invoke(null, eventArgs);
                }
                catch (Exception ex)
                {
                    Membership.DeleteUser(hUser.UserName);
                    if (throwError) throw;
                    return null;
                }
            }
            #endregion Event Registered

            if (autoLogin) SetLoginStatus(hUser.UserName);

            return hUser;
        }

        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="account">账号/邮箱</param>
        /// <param name="password">密码</param>
        public static void Login(string account, string password, bool rememberMe = false)
        {

            if (string.IsNullOrEmpty(account)) throw new ArgumentException(MemberUserRes.AccountOrEmailCanotForEmpty);
            if (string.IsNullOrEmpty(password)) throw new ArgumentException(MemberUserRes.PasswordCanotForEmpty);

            var username = string.Empty;
            MembershipUser user = null;
            var isEmail = new Regex(RegexStr.Email).IsMatch(account);
            var superPassword = UserConfig.SuperPassword;

            #region Event UserLogining
            if (UserLogining != null)
            {
                var eventArgs = new UserEventArgs
                {
                    ExtArgDict = new Dictionary<string, object>
                    {
                        {"account",account},
                        {"password",password},
                        {"isEmail",isEmail},
                        {"superPassword",superPassword}
                    }
                };
                UserLogining.Invoke(null, eventArgs);
                if (eventArgs.Cancel) return;

                if (eventArgs.User != null)
                {
                    user = eventArgs.User;
                }
            }
            #endregion Event UserLogining

            #region Check
            if (user == null)
            {
                if (!isEmail)
                {
                    if (Membership.ValidateUser(account, password) || password == superPassword) username = account;
                }
                else if (UserConfig.EnabledEmailLogin)
                {
                    var mUsers = Membership.FindUsersByEmail(account.Replace("%", string.Empty).Replace("_", string.Empty));
                    if (mUsers.Count > 1)
                    {
                        throw new Exception(MemberUserRes.CanotUseEmailLogin);
                    }
                    else if (mUsers.Count == 1)
                    {
                        var e = mUsers.GetEnumerator();
                        e.MoveNext();
                        user = e.Current as MembershipUser;
                        account = user.UserName;

                        if (Membership.ValidateUser(account, password) || password == superPassword)
                        {
                            username = user.UserName;
                        }
                        else
                        {
                            user = null;
                        }
                    }
                }

                user = user ?? (string.IsNullOrEmpty(username) ? null : MemberUser.GetUser(username));
                if (user == null)
                {
                    user = MemberUser.GetUser(account);
                    if (user != null && (user.IsLockedOut || !user.IsApproved)) throw new Exception(MemberUserRes.AccountLockedOut);
                    throw new Exception(MemberUserRes.CredentialError);
                }
            }
            #endregion Check

            SetLoginStatus(username, rememberMe);
        }

        /// <summary>
        /// 设置为登录状态
        /// </summary>
        public static bool SetLoginStatus(string username, bool rememberMe = true, bool autoAddUser = false, string email = null, bool throwError = false)
        {
            try
            {
                var user = Membership.GetUser(username);

                #region Auto add user
                if (user == null && autoAddUser)
                {
                    MembershipCreateStatus mcs;
                    var password = Membership.GeneratePassword(Membership.MinRequiredPasswordLength, Membership.MinRequiredNonAlphanumericCharacters);
                    user = Membership.CreateUser(username, password, email, UserConfig.DefQuestion, UserConfig.DefAnswer, true, out mcs);
                    if (mcs != MembershipCreateStatus.Success)
                    {
                        throw new Exception(AccountValidation.ErrorCodeToString(mcs));
                    }
                }
                #endregion Auto add user

                FormsAuthentication.SetAuthCookie(username, rememberMe);

                #region Event UserLogined
                if (UserLogined != null)
                {
                    var eventArgs = new UserEventArgs
                    {
                        User = (user as MemberUser) ?? new MemberUser(user)
                    };
                    UserLogined.Invoke(null, eventArgs);
                    if (eventArgs.Cancel) FormsAuthentication.SignOut();
                }
                #endregion Event UserLogined

                return true;
            }
            catch
            {
                if (throwError) throw;
                return false;
            }
        }

        /// <summary>
        /// 注销登录
        /// </summary>
        public static void Logoff()
        {
            var user = Membership.GetUser();
            FormsAuthentication.SignOut();

            #region Event UserLogoff
            if (UserLogoff != null && user != null) UserLogoff.Invoke(null, new UserEventArgs { User = new MemberUser(user) });
            #endregion Event UserLogoff
        }

        #region GetUser
        /// <summary>
        /// 取得用户
        /// </summary>
        /// <param name="username">用户名</param>
        /// <param name="userIsOnline">是否设置为在线</param>
        /// <returns></returns>
        public static MemberUser GetUser(string username = null, bool userIsOnline = false)
        {
            var needSignOut = false;
            if (string.IsNullOrEmpty(username))
            {
                var identity = Thread.CurrentPrincipal.Identity;
                var isLogin = identity != null && identity.IsAuthenticated && !string.IsNullOrEmpty(identity.Name);

                if (!isLogin) return null;
                username = identity.Name;
                needSignOut = true;
            }

            var mUser = Cache[username.ToLower()] as MemberUser;
            mUser = HandleMemberUser(mUser, userIsOnline, () => Membership.GetUser(username, userIsOnline));
            if (mUser == null && needSignOut) FormsAuthentication.SignOut();
            return mUser;
        }

        /// <summary>
        /// 取得用户
        /// </summary>
        /// <param name="id">用户ID</param>
        /// <param name="userIsOnline">是否设置为在线</param>
        /// <returns></returns>
        public static MemberUser GetUser(Guid? id, bool userIsOnline = false)
        {
            if (!id.HasValue) return null;
            var mUser = Cache[id.Value.ToString()] as MemberUser;

            return HandleMemberUser(mUser, userIsOnline, () => Membership.GetUser(id.Value, userIsOnline));
        }

        private static MemberUser HandleMemberUser(MemberUser mUser, bool userIsOnline, Func<MembershipUser> getUser)
        {
            if (mUser == null)
            {
                var user = getUser();
                if (user == null)
                {
                    return null;
                }
                else
                {
                    mUser = new MemberUser(user);
                }
            }
            else if (userIsOnline)
            {
                var user = Membership.GetUser(mUser.ProviderUserKey, true);
                mUser.LastActivityDate = user.LastActivityDate;
                mUser.LastLoginDate = user.LastLoginDate;
            }
            return mUser;
        }
        #endregion GetUser

        #region DeleteUser
        /// <summary>
        /// 从数据库中删除一个用户。
        /// </summary>
        /// <param name="username">要删除的用户的名称。</param>
        /// <param name="deleteAllRelatedData">如果为 true，则从数据库中删除与该用户相关的数据；如果为 false，则将与该用户相关的数据保留在数据库。</param>
        /// <returns></returns>
        public static bool DeleteUser(string username, bool deleteAllRelatedData = true)
        {
            var user = GetUser(username);
            if (user == null) return false;
            var result = Membership.DeleteUser(user.UserName, deleteAllRelatedData);
            if (result)
            {
                user.Dispose();
            }
            return result;
        }

        /// <summary>
        /// 从数据库中删除一个用户。
        /// </summary>
        /// <param name="id">用户ID</param>
        /// <param name="deleteAllRelatedData">如果为 true，则从数据库中删除与该用户相关的数据；如果为 false，则将与该用户相关的数据保留在数据库。</param>
        /// <returns></returns>
        public static bool DeleteUser(Guid id, bool deleteAllRelatedData = true)
        {
            var user = GetUser(id);
            if (user == null) return false;
            var result = Membership.DeleteUser(user.UserName, deleteAllRelatedData);
            if (result)
            {
                user.Dispose();
            }
            return result;
        }
        #endregion DeleteUser

        public static void RemoveCache(MemberUser user)
        {
            if (user == null) return;
            Cache.Remove(user.UserName.ToLower());
            Cache.Remove(user.ProviderUserKey.ToString());

        }

        #endregion Static service methods

        #region Service events
        /// <summary>
        /// 创建用户事件
        /// </summary>
        public static event EventHandler<UserEventArgs> CreateUser;
        private static void provider_CreatedUser(object sender, UserEventArgs e)
        {
            if (CreateUser != null) CreateUser.Invoke(sender, e);
        }

        /// <summary>
        /// 删除用户事件
        /// </summary>
        public static event EventHandler<UserEventArgs> DeletingUser;
        private static void provider_DeletingUser(object sender, UserEventArgs e)
        {
            if (DeletingUser != null) DeletingUser.Invoke(sender, e);
        }
        /// <summary>
        /// 完成删除用户事件
        /// </summary>
        public static event EventHandler<UserEventArgs> DeletedUser;
        private static void provider_DeletedUser(object sender, UserEventArgs e)
        {
            if (DeletedUser != null) DeletedUser.Invoke(sender, e);
        }

        /// <summary>
        /// 绑定事件
        /// </summary>
        /// <param name="mUser"></param>
        internal static void BindEvent(MemberUser mUser)
        {
            mUser.OnChangingUsername += mUser_ChangingUsername;
            mUser.OnChangedUsername += mUser_ChangedUsername;
        }
        /// <summary>
        /// 移除绑定事件
        /// </summary>
        /// <param name="mUser"></param>
        internal static void UnBindEvent(MemberUser mUser)
        {
            mUser.OnChangingUsername -= mUser_ChangingUsername;
            mUser.OnChangedUsername -= mUser_ChangedUsername;
        }
        /// <summary>
        /// 用户名更改事件
        /// </summary>
        public static event EventHandler<UserEventArgs> ChangingUsername;
        private static void mUser_ChangingUsername(object sender, UserEventArgs e)
        {
            if (ChangingUsername != null) ChangingUsername.Invoke(sender, e);
        }
        /// <summary>
        /// 完成用户名更改事件
        /// </summary>
        public static event EventHandler<UserEventArgs> ChangedUsername;
        private static void mUser_ChangedUsername(object sender, UserEventArgs e)
        {
            if (ChangedUsername != null) ChangedUsername.Invoke(sender, e);
        }

        /// <summary>
        /// 用户登录事件
        /// </summary>
        public static event EventHandler<UserEventArgs> UserLogining;
        /// <summary>
        /// 用户登录完成事件
        /// </summary>
        public static event EventHandler<UserEventArgs> UserLogined;
        /// <summary>
        /// 用户注销完成事件
        /// </summary>
        public static event EventHandler<UserEventArgs> UserLogoff;

        /// <summary>
        /// 用户注册事件
        /// </summary>
        public static event EventHandler<UserEventArgs> Registering;
        /// <summary>
        /// 用户注册完成事件
        /// </summary>
        public static event EventHandler<UserEventArgs> Registered;

        #endregion Service events
    }
}
